package algocity.modelo.mapa;

import algocity.modelo.energia.EnergiaInsuficienteException;
import algocity.modelo.energia.Energizador;
import algocity.modelo.energia.EnergizadorNulo;
import algocity.modelo.juego.Llamador;
import algocity.modelo.juego.LlamadorMaestro;
import algocity.modelo.juego.ObjetoNulo;
import algocity.modelo.necesidades.Conexion;
import algocity.modelo.zona.Construccion;
import algocity.modelo.zona.SinConstruccion;
import algocity.modelo.zona.Superficie;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class Hectarea
    implements Construible, Serializable {
    private final Posicion posicion;
    private Superficie superficie;
    private Construccion construccion;
    private Collection<Conexion> conexionesDisponibles = new ArrayList<Conexion>();

    private Energizador energizador;
    private Llamador<ObjetoNulo,ObjetoNulo> llamador;
    private Collection<Operador<ObjetoNulo>> deregistracionDiferida;


    public Hectarea(Posicion posicion, Superficie superficie) {
        this.posicion = posicion;
        this.superficie = superficie;
        new SinConstruccion().construite(this);
        this.energizador = EnergizadorNulo.getInstance();
        this.llamador = new LlamadorMaestro<>();
        this.deregistracionDiferida = new HashSet<>();
    }

    public Posicion getPosicion() {
        return posicion;
    }

    public Construccion getConstruccion() {
        return construccion;
    }

    public void setConstruccion(Construccion construccion) {
        // ToDo validar que no haya otra construcción
        this.construccion = construccion;
    }

    public Superficie getSuperficie() {
            return superficie;
    }

    public void agregarConexion(Conexion c){
        conexionesDisponibles.add(c);
    }

    public void setSuperficie (Superficie superficie){
    	this.superficie= superficie;
    	
    }
    
    public Collection<Conexion> getConexionesDisponibles() {
    	return this.conexionesDisponibles;
    }

    public boolean estaEnergizada() {
        // ToDo conflicto, no se pestablece si puede energizarse una hectárea con Agua
        return energizador.estaEnergizada();
    }

    public void ahoraTieneEnergia(Energizador energizador) {
        // ToDo conflicto a resolver cuando la hectárea queda en la intersección de 2 centrales
        this.energizador = energizador;
        llamador.llamar(ObjetoNulo.getInstance());

        // Sabemos que los avisadores registrados pueden deregistrarse al momento de ser
        // llamados si logran extraer energía de la central, lo cual provoca un error
        // de modificación concurrente
        for( Operador<ObjetoNulo> avisadorEnergizacion  : deregistracionDiferida ) {
            llamador.deregistrar(avisadorEnergizacion);
        }
    }

    public int consultarEnergia() {
        return energizador.consultarEnergiaRestante();
    }

    public void extraerEnergia(int consumoElectrico) throws EnergiaInsuficienteException {
        energizador.dameEnergía(consumoElectrico);
    }

    @Override
    public void registrarAvisoEnergizacion(Operador<ObjetoNulo> avisadorEnergizacion)
    {
        this.llamador.registrar(avisadorEnergizacion);
    }

    @Override
    public void deregistrarAvisoEnergizacion(Operador<ObjetoNulo> avisadorEnergizacion)
    {
        this.deregistracionDiferida.add(avisadorEnergizacion);
    }

    public void compensarEnergia(int energia) {
            this.energizador.compensarEnergia(energia);
    }

    public boolean esTerrenoBaldio() {
        return getConstruccion().esTerrenoBaldio();
    }

    public boolean equals(Object otroObjeto) {
        if (this == otroObjeto) {
            return true;
        }

        if (!(otroObjeto instanceof Hectarea)) {
            return false;
        }

        Hectarea otraHectarea = (Hectarea) otroObjeto;

        return posicion.equals(otraHectarea.posicion) &&
                superficie == otraHectarea.superficie &&   // Las superficies son singletons
                construccion.equals(otraHectarea.construccion) &&
                conexionesDisponibles.equals(otraHectarea.conexionesDisponibles) &&
                energizador.equals(otraHectarea.energizador);
    }

}
